//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "Generator.h"
#include "message_m.h"
#include "message_k.h"
#include <cxmlelement.h>
#include <Request.h>
#include <algorithm>
#include <cstdlib>

Define_Module(Generator)
;

Generator::Generator()
{
}

Generator::~Generator()
{
}

void Generator::initialize()
{
	minSimuTimeSlot = par("generatorMinSimuTimeSlot");
	simtime_t displayTime = minSimuTimeSlot;
	cXMLElement *requestsXml = par("requestsXml").xmlValue();
	for (cXMLElement *requestXml = requestsXml->getFirstChild(); requestXml; requestXml
			= requestXml->getNextSibling())
	{
		Request *request = new Request(requestXml);
		std::vector<Request*> *alRequests;
		if (mapStartTimeRequests.count(request->getStartTime()) > 0)
		{
			alRequests = mapStartTimeRequests[request->getStartTime()];
		}
		else
		{
			alRequests = new std::vector<Request*>();
			mapStartTimeRequests[request->getStartTime()] = alRequests;
		}
		alRequests->push_back(request);
		if (alStartTimes.size() == 0 || (alStartTimes.size() > 0
				&& alStartTimes[alStartTimes.size() - 1]
						!= request->getStartTime()))
		{
			alStartTimes.push_back(request->getStartTime());
		}
	}
	//	std::sort(alStartTimes.begin(), alStartTimes.end());
	std::reverse(alStartTimes.begin(), alStartTimes.end());
	generateDataMsg = new cMessage(NULL, GenerateDataMsgKind);
	//	for (int i = 0; i < alStartTimes.size() - 1; i++)
	//	{
	//		alStartTimes[i] = alStartTimes[i] - alStartTimes[i + 1];
	//	}
	if (alStartTimes.size() > 0)
	{
		sendEndSimuMsgTime = alStartTimes[0] + minSimuTimeSlot;
		simtime_t nextMsgStartTime = minSimuTimeSlot
				* alStartTimes[alStartTimes.size() - 1];
		alStartTimes.pop_back();
		scheduleAt(nextMsgStartTime, generateDataMsg);
	}
}

void Generator::handleMessage(cMessage *msg)
{
	EV<< "generator received message " << simTime() << endl;
	switch(msg->getKind())
	{
		case GenerateDataMsgKind:
			{
				int startTime = simTime() / minSimuTimeSlot;
				std::vector<Request*>* alRequests = mapStartTimeRequests[startTime];
				count = alRequests->size();
				EV << count << endl;
				for (int i = 0; i < alRequests->size(); i++)
				{
					Request* request = alRequests->at(i);
					mapIdRequests[request->getId()] = request;
					DataFromGeneratorMsg *dataPacket = new DataFromGeneratorMsg(NULL, DataFromGeneratorMsgKind);
					dataPacket->setDataPacketSize(request->getBitSize());
					dataPacket->setDataSendingTime(request->getDataSendingTime());
					dataPacket->setSenderId(request->getSenderId());
					dataPacket->setReceiverId(request->getReceiverId());
					dataPacket->setId(request->getId());
					EV << "send data packet " << request->getId() << endl;
					send(dataPacket, "hostDataOuts", request->getSenderId());
					alRequests->at(i) = NULL;
				}
				mapStartTimeRequests.erase(startTime);
				delete alRequests;
				if(alStartTimes.size() > 0)
				{
					simtime_t nextMsgStartTime = minSimuTimeSlot * alStartTimes[alStartTimes.size()- 1];
					alStartTimes.pop_back();
					scheduleAt(nextMsgStartTime, generateDataMsg);
				}
				else
				{
					// There is no more requests in the list, and send the endSimulationMsg to hosts
					// to inform them to stop at proper time
					for(int i = 0; i < gateSize("hostControlPorts"); i++)
					{
						EndSimuMsg *esMsg = new EndSimuMsg(NULL, EndSimuMsgKind);
						cGate *hostControlPort = gate("hostControlPorts$o", i);
						sendDelayed(esMsg, sendEndSimuMsgTime - simTime(), hostControlPort);
					}
				}
			}
			break;
	case RequestReceivedConfirmMsgKind:
		{
			if (dynamic_cast<RequestReceivedConfirmMsg *> (msg) != NULL)
			{
				RequestReceivedConfirmMsg* confirmMsg =
				check_and_cast<RequestReceivedConfirmMsg *>(msg);
				count--;
				if (confirmMsg->getConfirm() == true)
				{
					Request *request = mapIdRequests.at(confirmMsg->getId());
					mapIdRequests.erase(confirmMsg->getId());
					delete request;

					if(count == 0)
					{
						for(std::map<int, Request *>::iterator iter = mapIdRequests.begin(); iter != mapIdRequests.end(); iter++)
						{
							std::deque<Request *> *requestsQueue;
							if(mapSenderIdRequests.count(iter->second->getSenderId()) == 0)
							{
								requestsQueue = new std::deque<Request *>();
								mapSenderIdRequests[iter->second->getSenderId()] = requestsQueue;
							}
							requestsQueue->push_back(iter->second);
						}
						mapIdRequests.clear();
					}
				}
			}
			delete msg;
		}
		break;
	}
}
